﻿using System;
using System.ServiceModel;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System.Net;
using Fine.CRM.Common;
using ProxyClasses;


public class MimeAttachmentHandler : Plugin
{
    /// <summary>
    /// Initializes a new instance of the <see cref="MimeAttachmentHandler"/> class. Link up 
    /// event handlers for a Retrieve or Retrieve Multiple request to determine if files uploaded
    /// to Azure need to be retrieved as part of the request. Link up handler to clearn up Azure
    /// if an email attachment is deleted.
    /// </summary>
    public MimeAttachmentHandler()
        : base(typeof(MimeAttachmentHandler))
    {
        base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(40, MessageName.Retrieve, activitymimeattachment.GetLogicalName(), new Action<LocalPluginContext>(RetrievePostOp)));
        base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(40, MessageName.RetrieveMultiple, activitymimeattachment.GetLogicalName(), new Action<LocalPluginContext>(RetrievePostOp)));
        base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(40, MessageName.Delete, activitymimeattachment.GetLogicalName(), new Action<LocalPluginContext>(DeletePostOp)));
    }

    /// <summary>
    /// At the end of a Retrieve Multiple request download any required Azure blobs needed to fullfil the request.
    /// </summary>
    /// <param name="localContext"></param>
    protected void RetrievePostOp(LocalPluginContext localContext)
    {
        if (localContext == null)
        {
            throw new ArgumentNullException("localContext");
        }
        
        AzureSettings settings = new AzureSettings(localContext.XmlConfigurationResources);
        
        if (localContext.PluginExecutionContext.MessageName == MessageName.Retrieve)
        {
            if (localContext.PluginExecutionContext.OutputParameters.Contains("BusinessEntity"))
            {
                Entity detail = (Entity)localContext.PluginExecutionContext.OutputParameters["BusinessEntity"];
                string filename = detail.GetAttributeValue<string>(activitymimeattachment.Properties.filename);

                //if the filename attribute was included in the result set from CRM and the file name
                //contains the defined Azure file name prefix then the file data that CRM provided is placeholder
                //data and the actual file in sitting in an Azure blob.
                if (!string.IsNullOrEmpty(filename) && filename.StartsWith(settings.FileNamePrefix))
                {
                    //remove the prefix from the file name that is being displayed in the output. This 
                    //ensures that the downloaded file will have the correct file name.
                    filename = filename.Substring(settings.FileNamePrefix.Length);
                    detail.AddUpdateAttribute(activitymimeattachment.Properties.filename, filename);

                    //if the result from CRM contains the actual file body then go out to Azure and get
                    //the real document and replace the placeholder data from CRM with the real output.
                    if (detail.Contains(activitymimeattachment.Properties.body))
                    {
                        string azureBlob = GetAzureBlob(settings, detail.Id);
                        detail.AddUpdateAttribute(activitymimeattachment.Properties.body, azureBlob);
                    }
                }
            }          
        }
        else if (localContext.PluginExecutionContext.MessageName == MessageName.RetrieveMultiple)
        {
            if (localContext.PluginExecutionContext.OutputParameters.Contains("BusinessEntityCollection"))
            {
                EntityCollection result = (EntityCollection)localContext.PluginExecutionContext.OutputParameters["BusinessEntityCollection"];
                
                foreach (Entity detail in result.Entities)
                {
                    string filename = detail.GetAttributeValue<string>(activitymimeattachment.Properties.filename);
                    if (!string.IsNullOrEmpty(filename) && filename.StartsWith(settings.FileNamePrefix) && detail.Contains(activitymimeattachment.Properties.body))
                    {
                        string azureBlob = GetAzureBlob(settings, detail.Id);
                        detail.AddUpdateAttribute(activitymimeattachment.Properties.body, azureBlob);
                    }
                }
            }
        }
    }


    /// <summary>
    /// When an email attachment is deleted and the content of the attachment has been moved to Azure, delete
    /// the blob in the Azure cloud.
    /// </summary>
    /// <param name="localContext"></param>
    protected void DeletePostOp(LocalPluginContext localContext)
    {
        if (localContext.PreImage == null)
        {
            throw new NullReferenceException("'PreImage' is null.");
        }

        if (!localContext.PreImage.Contains(activitymimeattachment.Properties.filename))
        {
            throw new NullReferenceException("Plugin preimage is missing required parameter 'filename'");
        }

        AzureSettings settings = new AzureSettings(localContext.XmlConfigurationResources);

        if (localContext.PreImage.GetAttributeValue<string>(activitymimeattachment.Properties.filename).StartsWith(settings.FileNamePrefix))
        {
            AzureHelpers.BlobHelper blobHelper = new AzureHelpers.BlobHelper(settings.AccountName, settings.AccountKey);
            blobHelper.DeleteBlob(settings.ContainerName, localContext.PreImage.Id.ToString());
        }
    }

    /// <summary>
    /// Retrieve the attachment body (base64 encoded), mimetype, and filename from Azure blob storage. Return 
    /// as a single Tuple containing the three string values.
    /// </summary>
    /// <param name="settings"></param>
    /// <param name="mimeattachmentId"></param>
    /// <returns></returns>
    public static string GetAzureBlob(AzureSettings settings, Guid mimeattachmentId)
    {
        string blobName = mimeattachmentId.ToString();

        AzureHelpers.BlobHelper blobHelper = new AzureHelpers.BlobHelper(settings.AccountName,settings.AccountKey);
        
        string body = blobHelper.GetBlob(settings.ContainerName, blobName);

        return body;

    }

    /// <summary>
    /// Copy the attachment body, mimetype and filename to Azure blob storage. Uniquely identify the blob using
    /// the CRM attachement guid.
    /// </summary>
    /// <param name="settings"></param>
    /// <param name="mimeattachmentId"></param>
    /// <param name="body"></param>
    /// <param name="mimeType"></param>
    /// <param name="filename"></param>
    public static void PutAzureBlob(AzureSettings settings, Guid mimeattachmentId, string body)
    {
        string blobName = mimeattachmentId.ToString();

        AzureHelpers.BlobHelper blobHelper = new AzureHelpers.BlobHelper(settings.AccountName,settings.AccountKey);

        //if the target container does not exist then create it now
        List<string> containers = blobHelper.ListContainers();
        
        if (containers == null || !containers.Contains(settings.ContainerName))
        {
            blobHelper.CreateContainer(settings.ContainerName);
        }

        //upload the blob
        blobHelper.PutBlob(settings.ContainerName, blobName, body);

    }
}

